iT邦幫忙

2025 iThome 鐵人賽

DAY 23
0
Mobile Development

從零開始學習 iOS系列 第 23

從零開始學習 iOS Day22 - Push Notification II

  • 分享至 

  • xImage
  •  

在 Day 21,我們學會了如何 發送本地通知(Local Notification),並設定前景通知顯示。

今天要進一步延伸介紹:

  1. 點擊通知導向指定畫面
  2. 通知分類與互動按鈕

透過這篇文章,讓通知系統將不只是提醒,更能互動與導向 App 內特定頁面。


點擊通知開啟指定畫面

通知互動概念

當使用者點擊通知時,系統會將事件回傳給 App,我們可以在 UNUserNotificationCenterDelegate 的方法中處理:

func userNotificationCenter(_ center: UNUserNotificationCenter,
                            didReceive response: UNNotificationResponse,
                            withCompletionHandler completionHandler: @escaping () -> Void)
  • 可從 response.notification.request.identifier 得知是哪一個通知
  • 根據不同 identifier 執行不同操作(如導向特定頁面)

NotificationManager 調整

import SwiftUI
import UserNotifications

class NotificationManager: NSObject, ObservableObject, UNUserNotificationCenterDelegate {
    @Published var navigateToLearningPage = false

    override init() {
        super.init()
        UNUserNotificationCenter.current().delegate = self
        requestAuthorization()
    }

    private func requestAuthorization() {
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
            print(granted ? "使用者允許通知" : "使用者拒絕通知")
        }
    }

    func sendLocalNotification() {
        let content = UNMutableNotificationContent()
        content.title = "學習提醒"
        content.body = "該回來繼續學 Swift 鐵人賽囉!"
        content.sound = .default

        let request = UNNotificationRequest(
            identifier: "open_learning",
            content: content,
            trigger: UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
        )

        UNUserNotificationCenter.current().add(request)
    }

    // 前景顯示通知
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.banner, .sound])
    }

    // 點擊通知事件
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        let id = response.notification.request.identifier
        print("使用者點擊通知:\(id)")

        if id == "open_learning" {
            DispatchQueue.main.async {
                self.navigateToLearningPage = true
            }
        }
        completionHandler()
    }
}


SwiftUI 畫面導向範例

struct ContentView: View {
    @StateObject private var notificationManager = NotificationManager()

    var body: some View {
        NavigationStack {
            VStack(spacing: 20) {
                Text("Swift 鐵人賽 Day 21")
                    .font(.title2)

                Button("發送學習提醒通知") {
                    notificationManager.sendLocalNotification()
                }
                .buttonStyle(.borderedProminent)
            }
            .navigationDestination(isPresented: $notificationManager.navigateToLearningPage) {
                LearningView()
            }
        }
    }
}

struct LearningView: View {
    var body: some View {
        VStack(spacing: 16) {
            Text("📖 學習進度頁面")
                .font(.title)
            Text("歡迎回來,繼續挑戰 Swift 鐵人賽吧!")
        }
        .padding()
    }
}

https://github.com/jian-fu-hung/ithelp-2025/blob/main/image/Day22/%E6%88%AA%E5%9C%96%202025-10-07%20%E4%B8%8B%E5%8D%883.00.19.png?raw=true

https://github.com/jian-fu-hung/ithelp-2025/blob/main/image/Day22/%E6%88%AA%E5%9C%96%202025-10-07%20%E4%B8%8B%E5%8D%883.00.30.png?raw=true


通知分類與互動按鈕

什麼是通知分類(Category)與互動按鈕(Action)

通知分類(UNNotificationCategory):

  • 是一個通知的「類別」,可以用來綁定與使用者互動的按鈕。
  • 每個分類可以設定一組操作按鈕(UNNotificationAction

互動按鈕(UNNotificationAction):

  • 是使用者在通知上可以點擊的選項。
  • 可執行不同動作,例如:回覆訊息、標記完成、稍後提醒。

這樣可以讓 App 的通知系統更有彈性與互動性。

NotificationManager 調整

import SwiftUI
import UserNotifications

class NotificationManager: NSObject, ObservableObject, UNUserNotificationCenterDelegate {
    @Published var navigateToLearningPage = false

    override init() {
        super.init()
        UNUserNotificationCenter.current().delegate = self
        requestAuthorization()
        setupNotificationActions()
    }

    private func requestAuthorization() {
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
            print(granted ? "使用者允許通知" : "使用者拒絕通知")
        }
    }

    func sendLocalNotification() {
        let content = UNMutableNotificationContent()
        content.title = "學習提醒"
        content.body = "該回來繼續學 Swift 鐵人賽囉!"
        content.sound = .default

        let request = UNNotificationRequest(
            identifier: "open_learning",
            content: content,
            trigger: UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
        )

        UNUserNotificationCenter.current().add(request)
    }
    
    func setupNotificationActions() {
        let markDoneAction = UNNotificationAction(
            identifier: "MARK_DONE",
            title: "完成",
            options: [.authenticationRequired]
        )
        
        let remindLaterAction = UNNotificationAction(
            identifier: "REMIND_LATER",
            title: "稍後提醒",
            options: []
        )
        
        let taskCategory = UNNotificationCategory(
            identifier: "TASK_CATEGORY",
            actions: [markDoneAction, remindLaterAction],
            intentIdentifiers: [],
            options: []
        )
        
        UNUserNotificationCenter.current().setNotificationCategories([taskCategory])
    }
    
    func sendInteractiveNotification() {
        let content = UNMutableNotificationContent()
        content.title = "待辦任務"
        content.body = "今天的 Swift 鐵人賽任務還沒完成!"
        content.sound = .default
        content.categoryIdentifier = "TASK_CATEGORY" // 綁定分類
        
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
        let request = UNNotificationRequest(identifier: "TASK_NOTIFICATION", content: content, trigger: trigger)
        
        UNUserNotificationCenter.current().add(request)
    }

    // 前景顯示通知
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.banner, .sound])
    }

    // 點擊通知事件
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        let actionId = response.actionIdentifier
        let notificationId = response.notification.request.identifier
        
        print("使用者點擊通知:\(notificationId), 按鈕:\(actionId)")
        
        switch actionId {
        case "MARK_DONE":
            print("任務已完成")
        case "REMIND_LATER":
            print("稍後提醒")
        case UNNotificationDefaultActionIdentifier:
            print("使用者點擊通知本身")
        default:
            break
        }
        
        completionHandler()
    }
}

SwiftUI調整

struct ContentView: View {
    @StateObject private var notificationManager = NotificationManager()
        
        var body: some View {
            NavigationStack {
                VStack(spacing: 20) {
                    Text("Swift 鐵人賽 Day 21")
                        .font(.title2)
                    
                    Button("發送學習提醒通知") {
                        notificationManager.sendLocalNotification()
                    }
                    .buttonStyle(.borderedProminent)
                    
                    Button("發送分類通知") {
                        notificationManager.sendInteractiveNotification()
                    }
                    .buttonStyle(.borderedProminent)
                }
                .navigationDestination(isPresented: $notificationManager.navigateToLearningPage) {
                    LearningView()
                }
            }
        }
}

struct LearningView: View {
    var body: some View {
        VStack(spacing: 16) {
            Text("學習進度頁面")
                .font(.title)
            Text("歡迎回來,繼續挑戰 Swift 鐵人賽吧!")
        }
        .padding()
    }
}

https://github.com/jian-fu-hung/ithelp-2025/blob/main/image/Day22/%E6%88%AA%E5%9C%96%202025-10-07%20%E4%B8%8B%E5%8D%884.18.21.png?raw=true

今日小結

今天我們學到:

  • 點擊通知導向頁面:提升使用者體驗。
  • 通知分類與互動按鈕:讓通知更有互動性。

上一篇
從零開始學習 iOS Day21 - Push Notification
下一篇
從零開始學習 iOS Day23 - 多國語系
系列文
從零開始學習 iOS24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言